<template>
  <view
    class="chat-container"
    :class="{'show-keyboard': show_keyboard}"
    :style="{height: container_height}"
    @touchmove.prevent
  >
    <scroll-view
      class="chat-list"
      :class="{'show-board': show_board}"
      scroll-y
      scroll-with-animation
      :scroll-top="scrollTop"
      :style="{height: chat_scroll_height}"
      @scroll="handleScroll"
      @tap="hideChatBoard"
    >
      <view class="load-more chat" @tap="loadMoreChat">
        {{ chatParams.no_data ? '没有更多了...' : (chatParams.loading ? '努力加载中...' : '点击加载更多') }}
      </view>
      <chat-item
        v-for="item in chatList"
        :key="item['msg_id']"
        :message="item"
        :sender="sender"
        :receiver="receiver"
        :get-emoji-path="getEmojiPath"
        :format-date="unixToDate"
        @handle-click="handleTapMessage"
      />
    </scroll-view>
    <view class="chat-board" :style="{bottom: chat_board_bottom}">
      <view class="chat-input">
        <view class="icon-input emoji" @click="showChatEmoji"/>
        <input
          v-model="chatContent"
          class="chat-editor"
          confirm-type="send"
          confirm-hold
          :adjust-position="false"
          @focus="handleEditorFocus"
          @blur="handleEditorBlur"
          @confirm="handleEditorConfirm"
        />
        <view class="icon-input menu" @click="showChatMenu"/>
      </view>
      <view class="chat-menus" :style="{height: show_board ? '460rpx' : 0}">
        <chat-emojis
          v-if="show_chat_emoji"
          @click-emoji="handleAddEmoji"
          @back-message="handleBackMessage"
          class="chat-emojis"
        />
        <view v-if="show_chat_emoji" class="board-tools">
          <view class="item-tools emoji active"/>
          <view class="send-tools" @click="handleSendMessage('TEXT')">
            <text>发送</text>
          </view>
        </view>
        <view v-if="show_chat_menu" class="menus-list">
          <view class="item-menus" @tap="handleSelectImage('camera')">
            <view class="item-icon photo"/>
            <text class="item-label">拍摄照片</text>
          </view>
          <view class="item-menus" @tap="handleSelectImage('album')">
            <view class="item-icon photo-lib"/>
            <text class="item-label">选择照片</text>
          </view>
          <view class="item-menus" @tap="handleSelectVideo('camera')">
            <view class="item-icon video"/>
            <text class="item-label">拍摄视频</text>
          </view>
          <view class="item-menus" @tap="handleSelectVideo('album')">
            <view class="item-icon video-lib"/>
            <text class="item-label">选择视频</text>
          </view>
          <view class="item-menus" @tap="handleOpenBoardPopup('order')">
            <view class="item-icon order"/>
            <text class="item-label">订单</text>
          </view>
          <view class="item-menus" @tap="handleOpenBoardPopup('goods')">
            <view class="item-icon history"/>
            <text class="item-label">浏览</text>
          </view>
        </view>
      </view>
    </view>
    <u-popup
      v-model="show_board_popup"
      mode="bottom"
      border-radius="20"
      closeable
      safe-area-inset-bottom
      height="950rpx"
      class="board-popup"
    >
      <view class="board-popup--title">请选择您要咨询的内容</view>
      <view class="board-popup--tab">
        <view
          class="tab-item"
          :class="{active: board_popup_type === 'order'}"
          @tap="board_popup_type = 'order'"
        >我的订单</view>
        <view
          class="tab-item"
          :class="{active: board_popup_type === 'goods'}"
          @tap="board_popup_type = 'goods'"
        >我的浏览</view>
      </view>
      <scroll-view scroll-y class="board-popup--scroll">
        <view v-show="board_popup_type === 'order'" class="order-list">
          <view
            v-for="item in orderList"
            :key="item['sn']"
            class="order-item"
            @tap="handleSendMessage('ORDER', item['sn'])"
          >
            <view class="order-sn-date">
              <text>订单号：{{ item['sn'] }}</text>
              <text>{{ unixToDate(item['create_time'], 'yyyy-MM-dd hh:mm') }}</text>
            </view>
            <view class="goods-info">
              <scroll-view scroll-x class="scrollView" @touchmove.stop>
                  <view class="goods-info" id="order-goods-info">
                    <view v-for="img in item['sku_list']" :key="img.sku_id">
                      <img :src="img['goods_image']" class="goods-image" alt />
                    </view>
                  </view>
              </scroll-view>
            </view>
            <view class="goods-other">
              <view class="goods-price-status">
                <text class="goods-price">订单金额：￥{{ item['order_amount'] }}</text>
                <text class="goods-status"> {{ item['order_status_text'] }}</text>
              </view>
            </view>
            <view class="goods-hr"/>
          </view>
          <view class="load-more" @tap="loadMoreOrder">
            {{ orderParams.no_data ? '没有更多了...' : (orderParams.loading ? '努力加载中...' : '加载更多') }}
          </view>
        </view>
        <view v-show="board_popup_type === 'goods'" class="goods-list">
          <view
            v-for="(item, index) in goodsList"
            :key="index"
            class="goods-item"
            @tap="handleSendMessage('GOODS', item['goods_id'])"
          >
            <img :src="item['goods_img']" alt class="goods-image" />
            <view class="goods-other">
              <text class="goods-name">{{ item['goods_name'] }}</text>
              <text class="goods-price">￥{{ item['goods_price'] }}</text>
            </view>
            <view class="goods-hr" />
          </view>
          <view class="load-more" @tap="loadMoreGoods">
            {{ goodsParams.no_data ? '没有更多了...' : (goodsParams.loading ? '努力加载中...' : '加载更多') }}
          </view>
        </view>
      </scroll-view>
    </u-popup>
  </view>
</template>

<script>
import ChatApi from './ChatApi'
import ChatItem from './chat-item'
import ChatEmojis from './chat-emojis'
import emojiMixin from './emojiMixin'
import Cache, { Keys } from '../utils/cache'
import uPopup from '../uview-ui/components/u-popup/u-popup'
import { api } from '../config/config'
import JSONBig from '../common/json-bigint'
import websocket from './Websocket'

export default {
  name: 'chat',
  mixins: [emojiMixin],
  components: {
    ChatItem,
    ChatEmojis,
    'u-popup': uPopup
  },
  data() {
    return {
      chatApi: new ChatApi(''),
      receiver_id: '',
      receiver: '',
      sender: Cache.getItem(Keys.user),
      chatParams: {
        page_no: 1,
        page_size: 10,
        no_data: false,
        loading: false
      },
      chatList: [],

      orderParams: {
        page_no: 1,
        page_size: 10,
        no_data: false,
        loading: false
      },
      orderList: [],

      goodsParams: {
        page_no: 1,
        page_size: 10,
        no_data: false,
        loading: false
      },
      goodsList: [],

      chatContent: '',

      show_chat_menu: false,
      show_chat_emoji: false,
      show_keyboard: false,
      show_board_popup: false,

      board_popup_type: '',
      keyboard_height: 0,

      scrollTop: 0,
      oldScrollTop: 0,
    }
  },
  computed: {
    show_board() {
      return this.show_chat_emoji || this.show_chat_menu
    },
    // 聊天窗口高度
    chat_scroll_height() {
      let height = `calc(`
      // #ifndef H5
      height += `100vh - 110rpx`
      // #endif

      // #ifdef H5
      height += `${window.document.body.clientHeight}px - 88rpx - 110rpx`
      // #endif
      height += ` - ${this.keyboard_height}px`
      if (!this.show_keyboard) {
        height += ` - env(safe-area-inset-bottom)`
      }
      if (this.show_chat_emoji || this.show_chat_menu) {
        height += ` - 460rpx`
      }
      height += ')'
      return height
    },
    chat_board_bottom() {
      return this.keyboard_height + 'px'
    },
    // 内容区域高度
    container_height() {
      let height = `calc(100vh`
      height += ` - ${this.keyboard_height}px`
      height += ')'
      return height
    },
  },
  onLoad(params) {
    const { receiver_id, receiver_name } = params
    this.receiver_id = receiver_id
    uni.setNavigationBarTitle({ title: decodeURIComponent(receiver_name) })
    // 获取接收者信息
    this.getReceiverInfo().then(() => {
      // 创建会话后，开始轮询获取新消息
      this.chatApi
        .createSession(this.receiver_id)
        .then(async () => {
          await this.getNewMessage()
          this.handleScrollToBottom()
        })
    })
    // 移除未读消息数量
    this.chatApi.removeUnreadNum(receiver_id)
    // 监听键盘高度变化
    uni['onKeyboardHeightChange'](this.handleKeyboardHeightChange)

    // #ifdef APP-PLUS
    this.$scope['$getAppWebview']()['setStyle']({
      softinputNavBar: 'none'
    })
    // #endif
  },
  onShow() {
    if (!websocket.isOpen()) {
      this.chatApi.getWebsocketToken().then(res => {
        websocket.init(api.im + '/im/ws?token=' + res)
      })
    }
    //处理websocket消息
    uni.$on('websocket-msg', this.handleSocketMessage)
    this.getNewMessage().then(this.handleScrollToBottom)
  },
  onHide() {
    uni.$off('websocket-msg', this.handleSocketMessage)
  },
  methods: {
    /**
     * 获取新消息|轮询
     */
    getNewMessage() {
      return new Promise(resolve => {
        this.chatApi.getNewMessage(this.receiver_id).then(res => {
          let num = 0
          res.data.forEach(item => {
            // 检查是否有和消息列表里不一样的项，有的话就push进去。
            if (!this.chatList.map(_item => _item['msg_id']).includes(item['msg_id'])) {
              this.chatList.push(item)
              num++
            }
          })
        }).finally(resolve)
      })
    },
    /**
     * 获取接收者信息
     */
    getReceiverInfo() {
      return new Promise(resolve => {
        this.chatApi.getFriends().then(res => {
          this.receiver = res.filter(item => item['friends_id'] === this.receiver_id)[0]
          resolve()
        })
      })
    },
    /**
     * 输入框获得焦点
     */
    handleEditorFocus() {
      this.show_keyboard = true
      this.show_chat_emoji = false
      this.show_chat_menu = false
      this.handleScrollToBottom()
    },
    /**
     * 输入框失去焦点
     */
    handleEditorBlur() {
      this.show_keyboard = false
    },
    /**
     * 输入框确认
     * @param event
     */
    handleEditorConfirm(event) {
      const { type, detail } = event
      if (type === 'confirm') {
        this.handleSendMessage('TEXT', detail.value)
        this.chatContent = ''
      }
    },
    /**
     * 显示表情面板
     */
    showChatEmoji() {
      this.show_chat_emoji = true
      this.show_chat_menu = false
      this.show_keyboard = false
      this.handleScrollToBottom()
      uni['hideKeyboard']()
    },
    /**
     * 显示菜单面板
     */
    showChatMenu() {
      this.show_chat_menu = true
      this.show_chat_emoji = false
      this.show_keyboard = false
      this.handleScrollToBottom()
      uni['hideKeyboard']()
    },
    /**
     * 隐藏面板
     */
    hideChatBoard() {
      this.show_chat_menu = false
      this.show_chat_emoji = false
    },
    /**
     * 添加emoji
     * @param emojiId
     */
    handleAddEmoji(emojiId) {
      this.chatContent += `[${emojiId}]`
    },
    /**
     * 删除字符
     */
    handleBackMessage() {
      const content = this.chatContent
      const lastChar = content.charAt(content.length - 1)
      let delLen = 1
      if (lastChar === ']') {
        delLen = 5
      }
      this.chatContent = content.substring(0, content.length - delLen)
    },
    /**
     * 发送消息
     */
    async handleSendMessage(type, content) {
      if (type === 'TEXT') {
        if (!content) content = this.chatContent
        content = content.replace(/\[e(\d+)]/g, '<img src="" alt="e$1" class="emoji-img"/>')
        if (!content.trim()) return
        this.chatContent = ''
      }
      if (type === 'ORDER' || type === 'GOODS') {
        this.show_board_popup = false
      }
      await this.chatApi.sendMessage(type, this.receiver_id, content)
      await this.getNewMessage()
      this.handleScrollToBottom()
    },
    /**
     * 选择照片
     * @param sourceType
     */
    handleSelectImage(sourceType) {
      uni.chooseImage({
        count: 1,
        sourceType: [sourceType],
        success: (res) => {
          this.uploadFile(res['tempFilePaths'][0], 'IMAGES')
        }
      })
    },
    /**
     * 选择视频
     * @param sourceType
     */
    handleSelectVideo(sourceType) {
      uni.chooseVideo({
        sourceType: [sourceType],
        success: (res) => {
          this.uploadFile(res['tempFilePath'], 'VIDEO')
        }
      })
    },
    /**
     * 获取消息历史
     * @returns {Promise<void>}
     */
    async getMessageHistory() {
      this.chatParams.loading = true
      try {
        this.chatParams.receiver_id = this.receiver_id
        const res = await this.chatApi.getChatHistories(this.chatParams)
        const data = res.data
        this.chatList.splice(0, 0, ...data)
        this.chatParams.no_data = data.length < this.chatParams.page_size
      } finally {
        this.chatParams.loading = false
      }
    },
    /**
     * 滚动到底部
     */
    handleScrollToBottom() {
      this.scrollTop = this.oldScrollTop
      setTimeout(() => {
        this.scrollTop = 99999999
      }, 300)
    },
    /**
     * 点击了消息
     * @param message
     */
    handleTapMessage(message) {
      if (message.type === 'GOODS') {
        uni.navigateTo({ url: `/goods-module/goods?goods_id=${message['goods_id']}` })
      } else if (message.type === 'ORDER') {
        uni.navigateTo({ url: `/order-module/order/order-detail?orderSn=${message['order_sn']}`, })
      } else if (message.type === 'IMAGES') {
        uni['previewImage']({
          current: 0,
          urls: [message.url],
          indicator: 'none'
        })
      }
    },
    /**
     * 上传文件，成功后调用handleSendMessage
     * @param filePath
     * @param type
     */
    uploadFile(filePath, type) {
      uni['showLoading']({ title: '上传中...' })
      uni.uploadFile({
        url: `${this.chatApi.api.buyer}/uploaders`,
        header: { Authorization: Cache.getItem(Keys.accessToken) },
        filePath: filePath,
        name: 'file',
        success: (res) => {
          const data = JSON.parse(res.data)
          this.handleSendMessage(type, data.url)
        },
        complete: uni['hideLoading']
      })
    },
    /**
     * 打开选择订单、商品弹窗
     * @param type
     */
    handleOpenBoardPopup(type) {
      this.hideChatBoard()
      this.board_popup_type = type
      this.show_board_popup = true
      !this.orderList.length && this.getOrderList()
      !this.goodsList.length && this.getGoodsList()
    },
    /**
     * 获取订单列表
     */
    getOrderList() {
      const { no_data, loading, page_size } = this.orderParams
      if (no_data || loading) return
      this.orderParams.loading = true
      this.chatApi.getOrderList(this.orderParams, this.receiver_id).then(res => {
        const data = res.data
        this.orderList.push(...data)
        this.orderParams.no_data = data.length < page_size
      }).finally(() => {
        this.orderParams.loading = false
      })
    },
    /**
     * 获取商品列表
     */
    getGoodsList() {
      const { no_data, loading, page_size } = this.goodsParams
      if (no_data || loading) return
      this.goodsParams.loading = true
      this.chatApi.getGoodsList(this.goodsParams).then(res => {
        const data = res.data
        this.goodsList.push(...data)
        this.goodsParams.no_data = data.length < page_size
      }).finally(() => {
        this.goodsParams.loading = false
      })
    },
    /**
     * 加载更多订单
     */
    loadMoreOrder() {
      const { no_data, loading } = this.orderParams
      if (no_data || loading) return
      this.orderParams.page_no += 1
      this.getOrderList()
    },
    /**
     * 加载更多商品
     */
    loadMoreGoods() {
      const { no_data, loading } = this.goodsParams
      if (no_data || loading) return
      this.goodsParams.page_no += 1
      this.getGoodsList()
    },
    /**
     * 加载更多聊天记录
     */
    loadMoreChat() {
      const { no_data, loading } = this.chatParams
      if (no_data || loading) return
      this.chatParams.page_no += 1
      this.getMessageHistory()
    },
    /**
     * 格式化时间
     * @param unix
     * @param format
     * @returns {string}
     */
    unixToDate(unix, format) {
      let _format = format || 'yyyy-MM-dd hh:mm:ss'
      const d = new Date(unix * 1000)
      const o = {
        'M+': d.getMonth() + 1,
        'd+': d.getDate(),
        'h+': d.getHours(),
        'm+': d.getMinutes(),
        's+': d.getSeconds(),
        'q+': Math.floor((d.getMonth() + 3) / 3),
        S: d.getMilliseconds()
      }
      if (/(y+)/.test(_format)) _format = _format.replace(RegExp.$1, (d.getFullYear() + '').substr(4 - RegExp.$1.length))
      for (const k in o) if (new RegExp('(' + k + ')').test(_format)) _format = _format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
      return _format
    },
    /**
     * 键盘高度发生变化
     * @param res
     */
    handleKeyboardHeightChange(res) {
      this.keyboard_height = res.height
    },
    /**
     * 聊天窗口滚动
     * @param event
     */
    handleScroll(event) {
      this.oldScrollTop = event.detail.scrollTop
    },
    /**
     * 处理socket消息
     * @param message
     */
    handleSocketMessage(message) {
      let msg = JSONBig.parse(message)
      if (msg.type === 'msg') {
        let num = 0
        let item = msg.payload
        let msgId = item.msg_id

        if (!this.chatList.map(_item => _item['msg_id']).includes(msgId)) {
          item.msg_id = msgId
          this.chatList.push(item)
          num++
        }

        if (num !== 0) {
          this.handleScrollToBottom()
          this.chatApi.removeUnreadNum(this.receiver_id)
        }
      }
    }
  },
  // 页面卸载时
  onUnload() {
    // 取消监听键盘键盘高度变化
    uni['offKeyboardHeightChange'](this.handleKeyboardHeightChange)
  }
}
</script>

<style lang="scss">
page {
  overflow-y: hidden;
  height: 100vh;
}
</style>

<style lang="scss" scoped>
@import "chat";
.board-popup--scroll .order-list {
  .goods-info {
    padding-left: 0rpx;
    border-radius: 0rpx;
  }
  .goods-other {
    display: flex;
    justify-content: flex-end;
    .goods-price-status {
      margin-right: 25rpx;
      padding-top: 5rpx;
      color: #252525;
      .goods-price {
        margin-right: 50rpx;
        font-weight: 500;
      }
      .goods-status {
        color: #999999;
      }
    }
  }
}

.scrollView {
  white-space: nowrap;
  width:100%;
  #order-goods-info {
    .goods-image {
      padding: 20rpx 30rpx 0rpx 0rpx;
    }
  }
}
</style>
